#ifndef DW_GRAFFITI_VERTEX_BUFFER_H
#	define DW_GRAFFITI_VERTEX_BUFFER_H

#include "hardwarebuffer.h"
#include "declaration.h"

namespace dw {
	namespace graffiti {

struct VertexBuffer
:	public HardwareBuffer< GL_ARRAY_BUFFER >
{
public :
	typedef HardwareBuffer< GL_ARRAY_BUFFER > base;

	VertexBuffer(buffer::update::frequency frequency, buffer::access::type access, const AttributeDeclaration* attributeDeclarations, dw::uint32 attributesCount)
	:	base(frequency, access)
	,	m_vertexSize(0)
	,	m_attributes(NULL)
	,	m_attributesCount(attributesCount)
	,	m_attributesHashcode(0)
	{
		dwAssert(attributeDeclarations != NULL, "Invalid parameter: attributeDeclarations is null");

		m_attributes = new AttributesProperties [attributesCount];

		for (dw::uint32 attributeIt = 0; attributeIt < attributesCount; ++attributeIt)
		{
			AttributesProperties& attribute = m_attributes[attributeIt];
			const AttributeDeclaration& ref = attributeDeclarations[attributeIt];

			dwAssert(!hasAttribute(ref), "Invalid declaration, multiple elements share the same semantic and semanticIndex");
			dwAssert(ref.semanticIndex < attribute::semantic::MaxIndex, "Invalid declaration, semanticIndex is out of bounds");

			attribute.declaration = ref;
			attribute.offset = m_vertexSize;

			m_vertexSize += attribute::type::size(ref.type);
			m_attributesHashcode |= attribute::semantic::getHashcode(ref.semantic, ref.semanticIndex);
		}
	}

	~VertexBuffer()
	{
		delete [] m_attributes;
	}

public : //! @name Commands
	//! @{
	void resize(dw::uint32 vertexCount)
	{
		base::resize(m_vertexSize, vertexCount);
	}
	//! @}

public : //! @name State
	//! @{
	dw::uint32 getAttributesHashcode() const { return m_attributesHashcode; }
	dw::uint32 getCount() const { return m_size / m_vertexSize; }
	dw::uint32 getVertexSize() const { return m_vertexSize; }
	dw::uint32 getAttributesCount() const { return m_attributesCount; }

	const AttributeDeclaration& getAttributeDeclaration(dw::uint32 index) const
	{
		dwAssert(index < m_attributesCount, "Invalid parameter: index is out of bounds");

		return m_attributes[index].declaration;
	}

	dw::uint32 getAttributeOffset(dw::uint32 index) const
	{
		dwAssert(index < m_attributesCount, "Invalid parameter: index is out of bounds");

		return m_attributes[index].offset;
	}

	bool hasAttribute(const AttributeDeclaration& attribute) const
	{
		dw::uint32 attributeHashcode = attribute::semantic::getHashcode(attribute.semantic, attribute.semanticIndex);

		return (m_attributesHashcode & attributeHashcode) == attributeHashcode;
	}
	//! @}

private : //! @name Types definitions
	//! @{
	struct AttributesProperties
	{
		AttributeDeclaration declaration;
		dw::uint32 offset;
	};
	//! @}

private : //! @name Data
	//! @{
	dw::uint32 m_vertexSize;
	dw::uint32 m_attributesCount;
	AttributesProperties* m_attributes;
	dw::uint32 m_attributesHashcode;
	//! @}
};

	} // namespace graffiti
} // namespace dw

#endif // !DW_GRAFFITI_VERTEX_BUFFER_H
